/*!
 Temelia - Sparse matrix examples. This implementation uses
 red black trees to handle keys from rows and columns.

 You can see code for implementation independent functions and
 some red black tree travels.

 Copyright (C) 2008 Ceata (http://ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "include/samples.h"
#include <temelia/sparse_matrix.h>

// Include this only to iterate over red black trees in sample3
#include <temelia/red_black_tree.h>

static void matrix_key_handler(void *key, int row, int column, void *context)
{
	PRINT("key %p, row %d, column %d\n", key, row, column);
}

static void matrix_internal_column_handler(void *key, void *context)
{
	sparse_matrix_key_t m_key = (sparse_matrix_key_t) key;
	PRINT("key %p, row %d, column %ld\n", sparse_matrix_key_get_key(m_key),
			sparse_matrix_key_get_index(m_key), (long) context);
}

static void matrix_internal_row_handler(void *key, void *context)
{
	sparse_matrix_key_t m_key = (sparse_matrix_key_t) key;
	PRINT("key %p, row %ld, column %d\n", sparse_matrix_key_get_key(m_key),
			(long) context, sparse_matrix_key_get_index(m_key));
}

static void sample1(int type)
{
	sparse_matrix_t sparse_matrix;

	PRINT("\n\n Sample 1 started \n\n");

	sparse_matrix = sparse_matrix_new(5, 5, 10, type);

	PRINT("Is empty %d\n", sparse_matrix_is_empty(sparse_matrix));
	PRINT("Is full %d\n", sparse_matrix_is_full(sparse_matrix));

	sparse_matrix_set_key_at(sparse_matrix, 0, 0, (void *) 0x100);
	sparse_matrix_set_key_at(sparse_matrix, 0, 3, (void *) 0x103);
	sparse_matrix_set_key_at(sparse_matrix, 0, 2, (void *) 0x102);
	sparse_matrix_set_key_at(sparse_matrix, 4, 0, (void *) 0x142);

	PRINT("Iterating on matrix on columns and then on rows\n");
	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, -1);
	PRINT("\n");

	PRINT("Iterating on matrix on rows and then on columns\n");
	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);
	PRINT("\n");

	sparse_matrix_delete(sparse_matrix);

	PRINT("\n\n Sample 1 ended \n\n");
}

static void sample2(int type)
{
	sparse_matrix_t sparse_matrix;

	PRINT("\n\n Sample 2 started \n\n");

	sparse_matrix = sparse_matrix_new(5, 5, 10, type);

	sparse_matrix_set_key_at(sparse_matrix, 0, 0, (void *) 0x100);
	sparse_matrix_set_key_at(sparse_matrix, 0, 3, (void *) 0x103);
	sparse_matrix_set_key_at(sparse_matrix, 0, 2, (void *) 0x102);
	sparse_matrix_set_key_at(sparse_matrix, 4, 0, (void *) 0x142);

	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);
	PRINT("\n");

	// Key value is updated only !! Make sure you free it before.
	//  - Obtain the reference with get_key_at and call it's destructor
	// In this example, I use unallocated pointers so no memory
	// leaks may appear.
	sparse_matrix_set_key_at(sparse_matrix, 4, 0, (void *) 1000);

	PRINT("%p %p %p %p\n", sparse_matrix_get_key_at(sparse_matrix, -1, 0, 0),
			sparse_matrix_get_key_at(sparse_matrix, 4, 4, 0),
			sparse_matrix_get_key_at(sparse_matrix, 0, 0, 0),
			sparse_matrix_get_key_at(sparse_matrix, 4, 0, 0));

	sparse_matrix_remove_key_at(sparse_matrix, 0, 0);
	sparse_matrix_remove_key_at(sparse_matrix, 4, 4);
	sparse_matrix_remove_key_at(sparse_matrix, 0, 2);
	sparse_matrix_remove_key_at(sparse_matrix, 0, 3);

	PRINT("%p %p %p\n", sparse_matrix_get_key_at(sparse_matrix, 0, 0, 0),
			sparse_matrix_get_key_at(sparse_matrix, 4, 4, 0),
			sparse_matrix_get_key_at(sparse_matrix, 4, 0, 0));

	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);
	PRINT("\n");

	sparse_matrix_delete(sparse_matrix);

	PRINT("\n\n Sample 2 ended \n\n");
}

static void sample3(int type)
{
	sparse_matrix_t sparse_matrix;
	sparse_matrix_t transpose;
	sparse_matrix_t clone;
	red_black_tree_t *rows, *columns;

	PRINT("\n\n Sample 3 started \n\n");

	sparse_matrix = sparse_matrix_new(5, 5, 10, type);

	sparse_matrix_set_key_at(sparse_matrix, 0, 0, (void *) 0x100);
	sparse_matrix_set_key_at(sparse_matrix, 0, 3, (void *) 0x103);
	sparse_matrix_set_key_at(sparse_matrix, 0, 2, (void *) 0x102);
	sparse_matrix_set_key_at(sparse_matrix, 4, 0, (void *) 0x142);

	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);
	PRINT("\n");

	PRINT("\nMatrix transposed\n");
	transpose = sparse_matrix_transpose(sparse_matrix);
	sparse_matrix_iterate(transpose, matrix_key_handler, NULL, 1);
	PRINT("\n");

	PRINT("\nMatrix cloned\n");
	clone = sparse_matrix_clone(transpose);
	sparse_matrix_iterate(clone, matrix_key_handler, NULL, 1);
	PRINT("\n");
	sparse_matrix_delete(clone);

	PRINT("\nMatrix cloned\n");
	clone = sparse_matrix_clone(sparse_matrix);
	sparse_matrix_iterate(clone, matrix_key_handler, NULL, 1);
	PRINT("\n");
	sparse_matrix_delete(clone);

	/*
	 * As I explained, each row/column is a red-black tree.
	 * We can access them via sparse_matrix_get_rows and
	 * sparse_matrix_get_columns.
	 */
	rows = sparse_matrix_get_rows(sparse_matrix);
	columns = sparse_matrix_get_columns(sparse_matrix);

	if (rows)
	{
		PRINT("\nIterating over first row\n");
		red_black_tree_inorder(rows[0], matrix_internal_row_handler, (void *) 0);
	}

	if (columns)
	{
		PRINT("\nIterating over first column\n");
		red_black_tree_inorder(columns[0], matrix_internal_column_handler,
				(void *) 0);
	}

	sparse_matrix_delete(sparse_matrix);
	sparse_matrix_delete(transpose);

	PRINT("\n\n Sample 3 ended \n\n");
}

static void sample4(int type)
{
	sparse_matrix_t sparse_matrix;
	red_black_tree_t *rows, *columns;

	PRINT("\n\n Sample 4 started \n\n");

	sparse_matrix = sparse_matrix_new(5, 5, 10, type);

	sparse_matrix_set_key_at(sparse_matrix, 0, 0, (void *) 0x100);
	sparse_matrix_set_key_at(sparse_matrix, 0, 3, (void *) 0x103);
	sparse_matrix_set_key_at(sparse_matrix, 0, 2, (void *) 0x102);
	sparse_matrix_set_key_at(sparse_matrix, 4, 0, (void *) 0x142);

	rows = sparse_matrix_get_rows(sparse_matrix);
	columns = sparse_matrix_get_columns(sparse_matrix);

	if (rows)
	{
		PRINT("\nIterating over first row\n");
		red_black_tree_inorder(rows[0], matrix_internal_row_handler, (void *) 0);
	}

	if (columns)
	{
		PRINT("\nIterating over first column\n");
		red_black_tree_inorder(columns[0], matrix_internal_column_handler,
				(void *) 0);
	}

	sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, -1);

	sparse_matrix_resize(sparse_matrix, 10, 10, 10);

	sparse_matrix_set_key_at(sparse_matrix, 5, 5, (void *) 0x155);
	sparse_matrix_set_key_at(sparse_matrix, 6, 8, (void *) 0x168);
	sparse_matrix_set_key_at(sparse_matrix, 8, 3, (void *) 0x183);
	sparse_matrix_set_key_at(sparse_matrix, 9, 0, (void *) 0x190);

	if (type & SPARSE_MATRIX_ROW_DENSE)
		sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, -1);
	else
		sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);

	// Resize again, with smaller dimensions
	PRINT("\nDoing resize\n");
	sparse_matrix_resize(sparse_matrix, 3, 3, 10);

	if (type & SPARSE_MATRIX_ROW_DENSE)
		sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, -1);
	else
		sparse_matrix_iterate(sparse_matrix, matrix_key_handler, NULL, 1);

	sparse_matrix_delete(sparse_matrix);

	PRINT("\n\n Sample 4 ended \n\n");
}

static void samples(int type)
{
	sample1(type);
	sample2(type);
	sample3(type);
	sample4(type);
}

void run_sparse_matrix_samples()
{
	//INIT(stdout);
	samples(SPARSE_MATRIX_ROW_DENSE);
	samples(SPARSE_MATRIX_COLUMN_DENSE);
	samples(SPARSE_MATRIX_ROW_DENSE | SPARSE_MATRIX_COLUMN_DENSE);
	//DESTROY();
}
